package com.mowanka.mokeng.app.util

import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.util.*

/**
 * 加密/解密/签名工具类
 */
internal object SignUtil {
    private val hexDigits =
        charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')

    /**
     * Map 签名算法
     *
     * 设所有发送或者接收到的数据为集合M，将集合M内非空参数值的参数按照参数名ASCII码从小到大排序（字典序），
     * 使用URL键值对的格式（即key1=value1&key2=value2…）拼接成字符串stringA。
     *
     * 特别注意以下重要规则：
     * ◆ 参数名ASCII码从小到大排序（字典序）；
     * ◆ 如果参数的值为空不参与签名；
     * ◆ 参数名区分大小写；
     * ◆ 验证调用返回或微信主动通知签名时，传送的sign参数不参与签名，将生成的签名与该sign值作校验。
     * ◆ 微信接口可能增加字段，验证签名时必须支持增加的扩展字段
     */
    fun getMapSign(map: Map<String, String?>): String {
        val list = ArrayList<String>()
        for ((key, value) in map) {
            if ("" != value) {
                list.add("$key=$value&")
            }
        }
        val size = list.size
        val arrayToSort = list.toTypedArray()
        Arrays.sort(arrayToSort, java.lang.String.CASE_INSENSITIVE_ORDER)
        val sb = StringBuilder()
        for (i in 0 until size) {
            sb.append(arrayToSort[i])
        }
        return md5(sb.toString().substring(0, sb.length - 1)).toUpperCase(Locale.getDefault())
    }

    private fun md5(string: String): String {
        return try {
            val md5 = MessageDigest.getInstance("MD5")
            val bytes = md5.digest(string.toByteArray(StandardCharsets.UTF_8))
            val chars = CharArray(bytes.size * 2)
            for (i in bytes.indices) {
                val b = bytes[i].toInt()
                chars[i * 2] = hexDigits[b and 0xF0 shr 4]
                chars[i * 2 + 1] = hexDigits[b and 0x0F]
            }
            String(chars)
        } catch (e: Exception) {
            e.printStackTrace()
            throw RuntimeException("md5加密失败,str=$string")
        }
    }

    fun hexStr2Str(hexStr: String): String {
        val str = "0123456789ABCDEF"
        val hexs = hexStr.toCharArray()
        val bytes = ByteArray(hexStr.length / 2)
        var n: Int
        for (i in bytes.indices) {
            n = str.indexOf(hexs[2 * i]) * 16
            n += str.indexOf(hexs[2 * i + 1])
            bytes[i] = (n and 0xff).toByte()
        }
        return String(bytes)
    }

    fun sqrt(nb: Int): IntArray {
        var n = nb
        n *= 98
        val ints = IntArray(2)
        for (i in kotlin.math.sqrt(n.toDouble()).toInt() downTo 2) {
            if (n % (i * i) == 0) {
                ints[0] = i
                n /= (i * i)
                break
            }
        }
        if (n > 1) ints[1] = n
        return ints
    }

    fun xor(a: String, b: String): String {
        val sb = StringBuilder()
        var i = 0

        while (i < a.length && i < b.length) {
            sb.append((a[i].toInt().xor(b[i].toInt())).toChar())
            i++
        }
        return sb.toString()
    }
}